import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.*;
import com.networknt.schema.uri.URIFetcher;
import com.networknt.schema.uri.URLFetcher;
import validator.MaxUniqueItemsKeyword;
import validator.MaxUtf8ByteLengthKeyword;
import validator.MinUtf8ByteLengthKeyword;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author wjj
 */
public class JsonSchemaValidator {

    // $id of the Amazon Product Type Definition Meta-Schema.
    private static final String SCHEMA_ID = "https://schemas.amazon.com/selling-partners/definitions/product-types/meta-schema/v1";

    // Local copy of the Amazon Product Type Definition Meta-Schema.
    private static final String META_SCHEMA_PATH = "E:\\yanjiu_json_schema\\hardware_clamp_vise_meta_schema.json";

    // Local copy of an instance of the Amazon Product Type Definition Meta-Schema.
    private static final String HARDWARE_SCHEMA_PATH = "E:\\yanjiu_json_schema\\hardware_clamp_vise.json";

    // Keywords that are informational only and do not require validation.
    private static final List<String> NON_VALIDATING_KEYWORDS = List.of("editable", "enumNames");

    private static JsonSchema HARDWAREJSONSCHEMA;

    private static JsonMetaSchema STANDARDMETASCHEMA;

    private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        // Standard JSON Schema 2019-09 that Amazon Product Type Definition Meta-Schema extends from.
        STANDARDMETASCHEMA = JsonMetaSchema.getV201909();

// Build Amazon Product Type Definition Meta Schema with the standard JSON Schema 2019-09 as the blueprint.
// Register custom keyword validation classes (see below).
        JsonMetaSchema metaSchema = JsonMetaSchema.builder(SCHEMA_ID, STANDARDMETASCHEMA)
                .addKeywords(NON_VALIDATING_KEYWORDS.stream().map(NonValidationKeyword::new)
                        .collect(Collectors.toSet()))
                .addKeyword(new MaxUniqueItemsKeyword())
                .addKeyword(new MaxUtf8ByteLengthKeyword())
                .addKeyword(new MinUtf8ByteLengthKeyword())
                .build();

        // URIFetcher to route meta-schema references to local copy.
        URIFetcher uriFetcher = uri -> {
            // Use the local copy of the meta-schema instead of retrieving from the web.
            if (SCHEMA_ID.equalsIgnoreCase(uri.toString())) {
                return Files.newInputStream(Paths.get(META_SCHEMA_PATH));
            }

            // Default to the existing fetcher for other schemas.
            return new URLFetcher().fetch(uri);
        };

// Build the JsonSchemaFactory.
        JsonSchemaFactory schemaFactory = new JsonSchemaFactory.Builder()
                .defaultMetaSchemaURI(SCHEMA_ID)
                .addMetaSchema(STANDARDMETASCHEMA)
                .addMetaSchema(metaSchema)
                .uriFetcher(uriFetcher, "https")
                .build();

// Create the JsonSchema instance.
        try {
            HARDWAREJSONSCHEMA = schemaFactory.getSchema(new String(Files.readAllBytes(Paths.get(HARDWARE_SCHEMA_PATH))));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static Set<ValidationMessage> validator(JsonNode node) {
        return HARDWAREJSONSCHEMA.validate(node);
    }

    public static Set<ValidationMessage> validator(String input) {
        try {
            JsonNode node = OBJECT_MAPPER.readTree(input);
            return validator(node);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}
